package com.zcyh.exam.service;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.zcyh.exam.annotation.Unique;
import com.zcyh.exam.config.LoginContext;
import com.zcyh.exam.config.exception.BusinessException;
import com.zcyh.exam.model.common.PageInfo;
import com.zcyh.exam.model.common.ResultCode;
import com.zcyh.exam.model.entity.BaseEntity;
import com.zcyh.exam.model.request.PageParam;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * @author zengjintao
 * @version 1.0
 * @create_at 2020/12/28 19:16
 */
public abstract class CrudService <M extends BaseMapper<T>, T> extends ServiceImpl<M, T> {
    @Autowired
    private LoginContext loginContext;

    public TableInfo getTable() {
        return SqlHelper.table(entityClass);
    }

    public boolean deleteById(Serializable id) {
        return  super.removeById(id);
    }

    /**
     * 单表分页查询
     * @param pageParam
     * @param entity
     * @return
     */
    public PageInfo<T> selectPage(PageParam pageParam, T entity) {
        return this.selectPage(pageParam, Wrappers.query(entity));
    }

    /**
     * 条件列表分页查询
     * @param pageParam
     * @param queryWrapper
     * @return
     */
    public PageInfo<T> selectPage(PageParam pageParam, Wrapper<T> queryWrapper) {
        PageInfo<T> pageInfo = new PageInfo();
        Integer pageNumber = pageParam.getPageNumber();
        Integer pageSize = pageParam.getPageSize();
        if (ObjectUtils.isEmpty(pageNumber) && ObjectUtils.isEmpty(pageSize)) {
            List<T> list = baseMapper.selectList(queryWrapper);
            pageInfo.setDataList(list);
            pageInfo.setTotal(ObjectUtils.isEmpty(list) ? 0 : list.size());
        } else {
            Page<T> page = new Page<>(pageNumber, pageSize);
            Page<T> listPage = super.page(page, queryWrapper);
            pageInfo.setTotal(listPage.getTotal());
            pageInfo.setDataList(page.getRecords());
        }
        return pageInfo;
    }

    public <V> PageInfo<V> selectPage(Page<V> page) {
        PageInfo<V> pageInfo = new PageInfo();
        pageInfo.setTotal(page.getTotal());
        pageInfo.setDataList(page.getRecords());
        return pageInfo;
    }

    public static <T,V> PageInfo<T> selectPage(Class<T> c, IPage<V> page) {
        List<T> collect = page.getRecords().stream().map(x -> {
            return BeanUtil.copyProperties(x, c);
        }).collect(Collectors.toList());

        PageInfo pageInfo = new PageInfo();
        long totalCount = page.getTotal();
        long loadCount = page.getSize();
        pageInfo.setTotal(totalCount);
        long l = totalCount % loadCount == 0 ? totalCount/loadCount : totalCount/loadCount + 1;
        pageInfo.setTotalPage((int)l);
//        pageInfo.setDataList(page.getRecords());
        pageInfo.setDataList(collect);
        return pageInfo;
    }

    @Override
    public boolean saveOrUpdate(T entity) {
        if (entity instanceof BaseEntity) {
            Date now = new Date();
            BaseEntity baseEntity = (BaseEntity) entity;
            Map uniqueFieldMap = this.getUniqueField(entity);
            BaseEntity result = null;
            if (uniqueFieldMap.size() > 0) {
                QueryWrapper queryWrapper = Wrappers.<T>query()
                        .select("id")
                        .allEq(uniqueFieldMap);
                result = (BaseEntity) super.getOne(queryWrapper);
            }
            if (ObjectUtils.isNotEmpty(result)) {
                ResultCode resultCode = new ResultCode(ResultCode.FAIL, "该数据已存在,请勿重复添加");
                if (baseEntity.getId() != null) {
                    // 修改的数据id 不一样，存在相同数据
                    if (!baseEntity.getId().equals(result.getId())) {
                        throw new BusinessException(resultCode);
                    }
                } else {
                    throw new BusinessException(resultCode);
                }
            }

            String id = baseEntity.getId();
            if (baseEntity.getId() == null) {
                baseEntity.setCreateBy(loginContext.getLoginInfo().getUserId());
                baseEntity.setCreateTime(now);
            } else {
                baseEntity.setUpdateTime(now);
                baseEntity.setUpdateBy(loginContext.getLoginInfo().getUserId());
            }
            return super.saveOrUpdate(entity);
        }
        throw new RuntimeException(entityClass + " must be extends BaseEntity");
    }

    /**
     * 获取需要进行唯一约束校验的字段
     * @return
     */
    private Map<String, Object> getUniqueField(T entity) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(entity.getClass());
        List<TableFieldInfo> fieldList = tableInfo.getFieldList();
        Map uniqueField = new HashMap<>();
        fieldList.forEach(tableFieldInfo -> {
            Field field = tableFieldInfo.getField();
            Unique unique = field.getAnnotation(Unique.class);
            if (ObjectUtils.isNotEmpty(unique)) {
                try {
                    field.setAccessible(true);
                    String column = ObjectUtils.isEmpty(unique.value()) ? field.getName() : unique.value();
                    uniqueField.put(column, field.get(entity));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        });
        return uniqueField;
    }
}
